home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / ARGONET / PD / GRAPHICS / GIF2RPC.SPK / source / c / gif2rpc < prev    next >
Text File  |  1996-01-14  |  64KB  |  2,051 lines

  1. /* gif2rpc.c
  2.  * AUTHOR:      Cy Booker, cy@cheepnis.demon.co.uk
  3.  * LICENSE:     FreeWare, Copyright (c) 1995 Cy Booker, but see below
  4.  * PURPOSE:     convert GIF file to RISC OS sprite
  5.  *
  6.  * the lzw decoding part contains code that came with the following header:
  7.  *
  8.  *================================================================================================
  9.  * DECODE.C - An LZW decoder for GIF
  10.  * Copyright (C) 1987, by Steven A. Bennett
  11.  *
  12.  * Permission is given by the author to freely redistribute and include
  13.  * this code in any program as long as this credit is given where due.
  14.  *
  15.  * In accordance with the above, I want to credit Steve Wilhite who wrote
  16.  * the code which this is heavily inspired by...
  17.  *
  18.  * GIF and 'Graphics Interchange Format' are trademarks (tm) of
  19.  * Compuserve, Incorporated, an H&R Block Company.
  20.  *
  21.  * Release Notes: This file contains a decoder routine for GIF images
  22.  * which is similar, structurally, to the original routine by Steve Wilhite.
  23.  * It is, however, somewhat noticably faster in most cases.
  24.  *================================================================================================
  25.  *
  26.  * the GIF decoding is based on the specification:
  27.  *
  28.  *================================================================================================
  29.  *                     GRAPHICS INTERCHANGE FORMAT(sm)
  30.  *                                Version 89a
  31.  *                          (c)1987,1988,1989,1990
  32.  *                                 Copyright
  33.  *                          CompuServe Incorporated
  34.  *                              Columbus, Ohio
  35.  *================================================================================================
  36.  *
  37.  * note that there is no patent infringement involved in DECODING gif files...
  38.  *
  39.  * everything else you can blame me (cjb) for
  40.  *
  41.  * oh yeah, usual disclaimers apply...
  42.  *
  43.  * CJB: 1995.10.01:     made processing code a library
  44.  *
  45.  * TODO: so 16bit output is *quickly* downgraded to 8/4/2/1 if ok to do so
  46.  */
  47.  
  48. #include <assert.h>
  49. #include <ctype.h>
  50. #include <locale.h>             /* so can force lower case */
  51. #include <setjmp.h>
  52. #include <stdlib.h>
  53. #include <stdio.h>
  54. #include <string.h>
  55.  
  56.  
  57. #include "OS:colourtrans.h"
  58. #include "OS:hourglass.h"
  59. #include "OS:macros.h"
  60. #include "OS:osfile.h"
  61. #include "OS:osspriteop.h"
  62.  
  63.  
  64. #include "gif2rpc:map8bpp.h"
  65. #include "gif2rpc:map16bpp.h"
  66. #include "gif2rpc:process_gif.h"
  67.  
  68. #include "16bpp_48bit.h.16bpp_48bit"
  69. #include "16bpp_66bit.h.16bpp_66bit"
  70. #include "8bpp.h.8bpp"
  71.  
  72.  
  73.  
  74. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  75.  */
  76.  
  77. #ifndef DEBUG_LZW
  78. #define DEBUG_LZW 0
  79. #endif /* DEBUG_LZW */
  80.  
  81. #define LZW_MAX_CODES                           (4095)
  82.  
  83. #define FAIL_NOT_GIF                            (10)
  84. #define FAIL_CORRUPT_LZW                        (11)
  85. #define FAIL_SHORT_FILE                         (12)
  86. #define FAIL_BAD_RECORD                         (13)
  87. #define FAIL_GRAPHIC_EXTENSION_SIZE             (14)
  88. #define FAIL_NO_IMAGE                           (15)
  89. #define FAIL_IMAGE_DIMENSIONS                   (16)
  90. #define FAIL_GRAPHIC_EXTENSION_TERMINATOR       (17)
  91. #define FAIL_INTERNAL_PROCESS_MASK              (18)
  92. #define FAIL_INTERNAL_PROCESS_IMAGE             (29)
  93. #define FAIL_NO_PALETTE                         (21)
  94. #define FAIL_INCOMPLETE_LAST_ROW                (22)
  95. #define FAIL_EXTRA_DATA_AFTER_IMAGE             (23)
  96. #define FAIL_BAD_EOF_IMAGE                      (24)
  97.  
  98. #define GIF2RPC_PARAMS          "gif2rpc$Options"               /* system variable */
  99. #define SPRITE_NAME             "gif2rpc"                       /* default sprite name */
  100.  
  101. #define VBUF                    (1024)                          /* byte size of log file buffer */
  102.  
  103. #define YESNO(B)                ((B) ? "yes" : "no")
  104.  
  105. #define MODE(LOG2BPP, XRES, YRES) ((os_mode)(\
  106.                      (((LOG2BPP) + 1) << osspriteop_TYPE_SHIFT)\
  107.                      | ((XRES) << osspriteop_YRES_SHIFT)\
  108.                      | ((YRES) << osspriteop_XRES_SHIFT)\
  109.                      | 1))
  110.  
  111. #define LSR(X, S)       (((unsigned int)(X)) >> (S))            /* portable */
  112.  
  113. #define WORD_WIDTH(P)  LSR((P) + 31, 5)                         /* P is BIT width */
  114.  
  115.  
  116.  
  117. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  118.  */
  119.  
  120. typedef struct {
  121.   byte                  *bitmap;
  122.   int                   line_length;
  123. } bitmapinfo;
  124.  
  125. typedef struct {
  126.   bitmapinfo    source;
  127.   bitmapinfo    dest;
  128.   int           bpp;
  129.   os_coord      pixel_size;
  130. } reducebitmap;
  131.  
  132.  
  133. typedef bool    (*process_gif_fn)(const process_gif *);
  134.  
  135.  
  136. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  137.  */
  138.  
  139. static void default_wimppalette(
  140.                 os_colour       *palette,
  141.                 int             lb_bpp);
  142. static byte fail(void);
  143. static process_gif_fn filter_fn(
  144.                 int     index);
  145. static byte get_byte(void);
  146. static byte get_image_byte(void);
  147. static byte get_image_byte_aux(void);
  148. static int get_word(void);
  149. static void gif_get_image_desc(void);
  150. static void gif_lzw_decode(void);
  151. static void gif_parse(void);
  152. static void gif_parse_extension(
  153.                 int     extension);
  154. static byte *gif_row(void);
  155. static void gif_skip_data(void);
  156. static void insert_bytes_before_image(
  157.                 osspriteop_area         *area,
  158.                 osspriteop_header       *sprite,
  159.                 int                     siz);
  160. static void load_file(void);
  161. static void log_filename(
  162.                 const char      *name);
  163. static int lzw_get_next_code(void);
  164. static void lzw_init(
  165.                 int     size);
  166. static void prepare_sprite(void);
  167. static void process_cli(
  168.                 char            **argv);
  169. static bool process_gif_32bpp(
  170.                 const process_gif       *p);
  171. static void process_mask(void);
  172. static void process_image(void);
  173. static void reduce_bitmap(
  174.                 const reducebitmap      *rb);
  175. static void reduce_sprite_lb_bpp(
  176.                 int             lb_bpp);
  177. static int stricmp(
  178.                 const char      *sleft,
  179.                 const char      *sright);
  180. static void turn_off_hourglass(void);
  181.  
  182.  
  183.  
  184. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  185.  */
  186.  
  187. static struct {
  188.   osspriteop_area       *area;
  189.   osspriteop_header     *sprite;
  190.   int                   lb_bpp;
  191.   os_coord              pixel_size;
  192.   os_coord              dpi;
  193.   bool                  transparent_p;
  194.   bitmapinfo            image;
  195.   bitmapinfo            mask;
  196.   int                   filter;         /* index into Gfilters array */
  197. }               Gsprite;
  198.  
  199. static struct GIF {
  200.   byte                  *file_buffer;
  201.   unsigned int          file_siz;
  202.   unsigned int          file_pos;
  203.   struct {
  204.     os_coord    size;                   /* size of screen, in pixels */
  205.     int         bpp;                    /* bpp of screen */
  206.     int         background;             /* background palette index (ignored) */
  207.     int         aspect_ratio;           /* weird format */
  208.     bool        palette_p;              /* was a global palette defined? */
  209.     bool        palette_sorted_p;       /* is palette pre-sorted? (ignored) */
  210.     int         palette_bpp;            /* how many bpp available in palette */
  211.     os_colour   palette[256];
  212.   }                     screen;
  213.   struct {
  214.     os_coord    topleft;
  215.     os_coord    size;                   /* technically, should be inside screen */
  216.     bool        interlace_p;
  217.     bool        palette_p;
  218.     bool        palette_sorted_p;
  219.     int         palette_bpp;
  220.     os_colour   palette[256];           /* this is the palette for the image */
  221.   }                     image;
  222.   struct {
  223.     int         dispose;                /* ignore */
  224.     bool        user_input_p;           /* ignore */
  225.     bool        transparent_p;          /* is the image transparent */
  226.     int         delay_time;             /* ignore */
  227.     int         transparent;            /* this is the transparent colour index */
  228.   }                     control;
  229.   bool                  image_p;
  230.   bool                  control_p;      /* control section found? */
  231.   int                   input_bpp;      /* ie for image/screen {1,2,3,4,5,6,7,8} */
  232.   int                   output_lb_bpp;  /* requested lb_bpp {0,1,2,3,4,5} */
  233.   jmp_buf               abortion;
  234. } Ggif;
  235.  
  236. static struct {
  237.   int           current_size;
  238.   int           clear;
  239.   int           ending;
  240.   int           newcodes;
  241.   int           top_slot;
  242.   int           slot;
  243.   int           nbits_left;
  244.   int           bad_code_count;
  245.   byte          b1;                             /* one byte bit buffer */
  246.   byte          stack[LZW_MAX_CODES + 1];       /* Stack for storing pixels */
  247.   byte          suffix[LZW_MAX_CODES + 1];      /* Suffix table */
  248.   unsigned int  prefix[LZW_MAX_CODES + 1];      /* Prefix linked list */
  249.   int           row;                            /* offset in current interlace bit */
  250.   int           interlace_part;                 /* 0..3 */
  251.   int           block_count;                    /* bytes left in current gif file block */
  252.   int           real_row;                       /* used by hourglass off */
  253. } Glzw;
  254.  
  255. static struct {
  256.   char          *gif;
  257.   char          *sprite;
  258.   char          *filter;
  259.   char          *log;
  260.   char          *name;
  261.   bool          help_p;
  262.   bool          square_p;
  263.   bool          fussy_p;
  264.   bool          quiet_p;
  265.   bool          hourglass_p;
  266.   bool          stats_p;
  267.   bool          accurate_p;
  268.   bool          force_66_p;
  269.   bool          autoname_p;
  270.   int           bpp;
  271.   int           verbose;
  272. } Gargs;
  273.  
  274. static process_gif      Gpg;
  275.  
  276. static FILE             *Glog = stdout;
  277.  
  278. static struct {
  279.   const char            *name;
  280.   process_gif_fn        fn_8bpp;
  281.   process_gif_fn        fn_16bpp_48bit;
  282.   process_gif_fn        fn_16bpp_66bit;
  283. }                       Gfilters[] = {
  284. #define FILTER(X)       {#X, process_gif_8bpp_ ##X, process_gif_16bpp_ ##X## _48bit, process_gif_16bpp_ ##X## _66bit}
  285.           /* first */   FILTER(nearest),
  286.           /* second */  FILTER(dither2x2),
  287.                         FILTER(sierra2_4a),
  288.                         FILTER(floyd_steinberg),
  289.                         FILTER(sierra3),
  290.                         FILTER(sierra2),
  291.                         FILTER(burkes),
  292.                         FILTER(stucki),
  293.                         FILTER(jarvis_judice_ninke)};
  294.  
  295.  
  296. static struct {
  297.   os_t          started;
  298.   os_t          decoded;
  299.   os_t          processed;
  300. }                       Gwhen;
  301.  
  302.  
  303.  
  304. static os_colour        Goutput_palette[256];
  305.  
  306.  
  307. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  308.  */
  309.  
  310. int main(
  311.                 int             argc,
  312.                 char            **argv) {
  313.   int           code;
  314.  
  315.   NOT_USED(argc);
  316.   setlocale(LC_ALL, "");                /* current locale, and not C locale */
  317.  
  318.   process_cli(argv);
  319.  
  320.   if (Gargs.stats_p) {
  321.     Gwhen.started = os_read_monotonic_time();
  322.   }
  323.   if (Gargs.log) {
  324.     if (!freopen(Gargs.log, "a", stderr)) {
  325.       fprintf(stderr, "Failed to redirect output\n");
  326.       return EXIT_FAILURE;
  327.     }
  328.     setvbuf(stderr, NULL, _IONBF, 0);
  329.     Glog = stderr;
  330.   }
  331.  
  332.   load_file();
  333.  
  334.   code = setjmp(Ggif.abortion);
  335.   if (code) {
  336.     if (!Gargs.quiet_p) {
  337.       fprintf(stderr, "gif2rpc: %s: error %d @ %#x\n", Gargs.gif, code, Ggif.file_pos);
  338.     }
  339.     return EXIT_FAILURE;
  340.   }
  341.  
  342.   gif_parse();
  343.  
  344.   return EXIT_SUCCESS;
  345. }
  346.  
  347.  
  348.  
  349. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  350.  */
  351.  
  352. static void add_arg(
  353.                 char            **pcli,
  354.                 const char      *arg) {
  355.   char  *cli;
  356.  
  357.   assert(pcli);
  358.   assert(arg);
  359.  
  360.   cli = (*pcli)
  361.         ? realloc(*pcli, strlen(*pcli) + 1 + strlen(arg) + 1)
  362.         : malloc(strlen(arg) + 1);
  363.   if (!cli) {
  364.     exit(EXIT_FAILURE);
  365.   }
  366.   if (*pcli) {
  367.     strcat(cli, " ");
  368.     strcat(cli, arg);
  369.   } else {
  370.     strcpy(cli, arg);
  371.   }
  372.   *pcli = cli;
  373. }
  374.  
  375.  
  376.  
  377. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  378.  */
  379.  
  380. static void gif_parse(void) {
  381.   bits          flags;
  382.   int           i, size;
  383.   os_colour     colour;
  384.   byte          record;
  385.   byte          dummy;
  386.   int           code;
  387.   int           bpp, siz, in_file_siz, out_file_siz, spr_file_siz;
  388.   bits          duration_decode, duration_process, duration_all;
  389.   os_error      *error;
  390.  
  391.   if ((get_byte() != 'G')
  392.       || (get_byte() != 'I')
  393.       || (get_byte() != 'F')) {
  394.     longjmp(Ggif.abortion, FAIL_NOT_GIF);
  395.   }
  396.   dummy = get_byte();
  397.   dummy = get_byte();
  398.   dummy = get_byte();                   /* skip version string */
  399.   /*
  400.    * read screen description
  401.    */
  402.   Ggif.screen.size.x = get_word();
  403.   Ggif.screen.size.y = get_word();
  404.   flags = get_byte();
  405.   Ggif.screen.bpp = LSR(flags & 0x70, 4) + 1;
  406.   Ggif.screen.palette_bpp = (flags & 0x07) + 1;
  407.   Ggif.screen.palette_p = ((flags & 0x80) != NONE);
  408.   Ggif.screen.palette_sorted_p = ((flags & 0x08) != NONE);
  409.   Ggif.screen.background = get_byte();
  410.   Ggif.screen.aspect_ratio = get_byte();
  411.   if (Ggif.screen.palette_p) {
  412.     /*
  413.      * global colour map
  414.      */
  415.     size = 1 << Ggif.screen.palette_bpp;
  416.     for (i= 0; (i < size); i++) {
  417.       colour = get_byte() << 8;                 /* should we do get_byte() * 255 / ((1 << palette_bpp) - 1) ? */
  418.       colour |= get_byte() << 16;
  419.       colour |= get_byte() << 24;
  420.       Ggif.screen.palette[i] = colour;
  421.     }
  422.   }
  423.   if (Gargs.verbose > 0) {
  424.     fprintf(Glog, "   screen size= (%d, %d), screen bpp= %d, palette_bpp= %d, background= %d, palette= %s, sorted= %s, aspect_ratio= %d\n",
  425.                 Ggif.screen.size.x, Ggif.screen.size.y,
  426.                 Ggif.screen.bpp,
  427.                 Ggif.screen.palette_bpp,
  428.                 Ggif.screen.background,
  429.                 YESNO(Ggif.screen.palette_p),
  430.                 YESNO(Ggif.screen.palette_sorted_p),
  431.                 Ggif.screen.aspect_ratio);
  432.   }
  433.   for (;;) {
  434.     record = get_byte();
  435.     switch (record) {
  436.     case 0x2c:
  437.       if (Ggif.image_p) {
  438.         /* ignore multiple images */
  439.         return;
  440.       }
  441.       gif_get_image_desc();
  442.       if ((Ggif.image.palette_p == NONE) && (Ggif.screen.palette_p == NONE)) {
  443.         longjmp(Ggif.abortion, FAIL_NO_PALETTE);
  444.       }
  445.       Ggif.image_p = TRUE;
  446.       Ggif.input_bpp = (Ggif.image.palette_p) ? Ggif.image.palette_bpp : Ggif.screen.palette_bpp;
  447.       prepare_sprite();
  448.       if (Gargs.hourglass_p) {
  449.         atexit(turn_off_hourglass);
  450.         xhourglass_on();
  451.       }
  452.       if (!Gargs.fussy_p) {
  453.         /*
  454.          * set up a dummy jump buffer to catch any and all errors in the decoding
  455.          */
  456.         code = setjmp(Ggif.abortion);
  457.         if (code == 0) {
  458.           gif_lzw_decode();
  459.         } else {
  460.           if (!Gargs.quiet_p) {
  461.             fprintf(stderr, "gif2rpc: %s: (ignored) error %d @ %#x\n", Gargs.gif, code, Ggif.file_pos);
  462.           }
  463.         }
  464.         /*
  465.          * repeat code in main to handle any processing bugs!
  466.          */
  467.         code = setjmp(Ggif.abortion);
  468.         if (code) {
  469.           if (!Gargs.quiet_p) {
  470.             fprintf(stderr, "gif2rpc: %s: error %d @ %#x\n", Gargs.gif, code, Ggif.file_pos);
  471.           }
  472.           exit(EXIT_FAILURE);
  473.         }
  474.       } else {
  475.         gif_lzw_decode();
  476.       }
  477.       if (Gargs.stats_p) {
  478.         Gwhen.decoded = os_read_monotonic_time();
  479.       }
  480.  
  481.       if (Gargs.hourglass_p) {
  482.         xhourglass_percentage(0);
  483.       }
  484.  
  485.       if (Gsprite.transparent_p) {
  486.         /*
  487.          * must generate a mask from raw 8-bit data before processing `image'
  488.          *
  489.          */
  490.         process_mask();
  491.       }
  492.       process_image();
  493.  
  494.       if (Gargs.verbose > 0) {
  495.         fprintf(Glog, " saving `%s', size %#x\n", Gargs.sprite, Gsprite.area->used);
  496.       }
  497.       if (Gargs.hourglass_p) {
  498.         xhourglass_percentage(0);
  499.       }
  500.  
  501.       if (Gargs.stats_p) {
  502.         Gwhen.processed = os_read_monotonic_time();
  503.       }
  504.  
  505.       /*
  506.        * save sprite file
  507.        */
  508.       error = xosspriteop_save_sprite_file(osspriteop_USER_AREA, Gsprite.area, Gargs.sprite);
  509.       if (error) {
  510.         if (!Gargs.quiet_p) {
  511.           fprintf(stderr, "gif2rpc: error %#x `%s' saving file `%s'\n",
  512.                         error->errnum, error->errmess, Gargs.sprite);
  513.         }
  514.       }
  515.  
  516.       if (Gargs.stats_p) {
  517.         duration_decode = Gwhen.decoded - Gwhen.started;
  518.         duration_process = Gwhen.processed - Gwhen.decoded;
  519.         duration_all = os_read_monotonic_time() - Gwhen.started;
  520.         fprintf(Glog, "gif2rpc ");
  521.         log_filename(Gargs.gif);
  522.         fprintf(Glog, "(%4dx%4d:%d%c%c.%c%c%c.%c) -> ",
  523.                         Gsprite.pixel_size.x,
  524.                         Gsprite.pixel_size.y,
  525.                         Ggif.input_bpp,
  526.                         (Ggif.screen.palette_p) ? 'p' : ' ',
  527.                         (Ggif.screen.palette_sorted_p) ? 's' : ' ',
  528.                         (Ggif.image.interlace_p) ? 'i' : ' ',
  529.                         (Ggif.image.palette_p) ? 'p' : ' ',
  530.                         (Ggif.image.palette_sorted_p) ? 's' : ' ',
  531.                         ((Ggif.control_p) && (Ggif.control.transparent_p)) ? 't' : ' ');
  532.         log_filename(Gargs.sprite);
  533.         in_file_siz  = Ggif.file_siz;
  534.         out_file_siz = Gsprite.area->used - 4;
  535.         bpp = 1 << ("01223333"[Ggif.input_bpp-1] - '0');
  536.         siz = Gsprite.pixel_size.y * 4 * WORD_WIDTH(Gsprite.pixel_size.x * bpp);
  537.         spr_file_siz = (sizeof(osspriteop_area) - 4)
  538.                        + sizeof(osspriteop_header)
  539.                        + (256 * sizeof(os_colour_pair))
  540.                        + siz
  541.                        + ((Gsprite.transparent_p) ? siz : 0);
  542.  
  543.         fprintf(Glog, "(%.3s) %8d %6.2f %6.2f%% %s\n",
  544.                 "  2  4 1625632K16M"+(3*Gsprite.lb_bpp),
  545.                 spr_file_siz - out_file_siz,
  546.                 duration_all / 100.0,
  547.                 (duration_process * 100.0) / (double)duration_all,
  548.                 Gfilters[Gsprite.filter].name);
  549.       }
  550.       if (!Gargs.fussy_p) {
  551.         /*
  552.          * don't bother checking for multiple images, or validating an end of file
  553.          */
  554.         return;
  555.       }
  556.       break;
  557.  
  558.     case 0x21:
  559.       gif_parse_extension(get_byte());
  560.       break;
  561.  
  562.     case 0x3b:           /* marks end of file */
  563.       if (!Ggif.image_p) {
  564.         longjmp(Ggif.abortion, FAIL_NO_IMAGE);
  565.       }
  566.       return;
  567.  
  568.     default:
  569.       if (!Gargs.quiet_p) {
  570.         fprintf(stderr, "gif2rpc: %s: Bad record %#x\n", Gargs.gif, record);
  571.       }
  572.       longjmp(Ggif.abortion, FAIL_BAD_RECORD);
  573.     }/* switch (record) */
  574.   }/* for (;;) */
  575. }
  576.  
  577.  
  578.  
  579. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  580.  */
  581.  
  582. static void gif_parse_extension(
  583.                 int     extension) {
  584.   int           size, i;
  585.   byte          buffer[256];
  586.  
  587.   switch (extension) {
  588.   case 0x00:                /* change sierra2_4a behaviour! */
  589.     break;
  590.  
  591.   case 0xf9:                /* graphic control */
  592.     if (Gargs.verbose > 1) {
  593.       fprintf(Glog, "   processing graphic control extension @ %#x\n", Ggif.file_pos);
  594.     }
  595.     size = get_byte();
  596.     if (size != 4) {
  597.       longjmp(Ggif.abortion, FAIL_GRAPHIC_EXTENSION_SIZE);
  598.     }
  599.     for (i= 0; (i < size); i++) {
  600.       buffer[i] = get_byte();
  601.     }
  602.     Ggif.control_p = TRUE;
  603.     Ggif.control.dispose = LSR(buffer[0] & 0x1c, 2);
  604.     Ggif.control.user_input_p = ((buffer[0] & 0x02) != NONE);
  605.     Ggif.control.transparent_p = ((buffer[0] & 0x01) != NONE);
  606.     Ggif.control.delay_time = buffer[1] + (256 * buffer[2]);
  607.     if (Ggif.control.transparent_p) {
  608.       Ggif.control.transparent = buffer[3];
  609.     }
  610.     if (get_byte() != 0) {
  611.       longjmp(Ggif.abortion, FAIL_GRAPHIC_EXTENSION_TERMINATOR);
  612.     }
  613.     break;
  614.  
  615.   case 0xfe:                /* comment, in 7-bit ascii */
  616.     if (Gargs.verbose > 1) {
  617.       fprintf(Glog, "    skipping comment extension @ %#x\n", Ggif.file_pos);
  618.     }
  619.     gif_skip_data();
  620.     break;
  621.  
  622.   case 0x01:                /* plain text [a built in text `image'] */
  623.     if (Gargs.verbose > 1) {
  624.       fprintf(Glog, "    skipping text extension @ %#x\n", Ggif.file_pos);
  625.     }
  626.     gif_skip_data();
  627.     break;
  628.  
  629.   case 0xff:                /* application */
  630.     if (Gargs.verbose > 1) {
  631.       fprintf(Glog, "    skipping application extension @ %#x\n", Ggif.file_pos);
  632.     }
  633.     gif_skip_data();
  634.     break;
  635.  
  636.   default:
  637.     if (Gargs.verbose > 1) {
  638.       fprintf(Glog, "    skipping unknown extension %d @ %#x\n", extension, Ggif.file_pos);
  639.     }
  640.     gif_skip_data();
  641.   }
  642. }
  643.  
  644.  
  645.  
  646. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  647.  */
  648.  
  649. static void gif_get_image_desc(void) {
  650.   bits          flags;
  651.   int           i, size;
  652.   os_colour     colour;
  653.  
  654.   if (Gargs.verbose > 1) {
  655.     fprintf(Glog, "    reading image description @ %#x\n", Ggif.file_pos);
  656.   }
  657.   Ggif.image.topleft.x = get_word();
  658.   Ggif.image.topleft.y = get_word();
  659.   Ggif.image.size.x    = get_word();
  660.   Ggif.image.size.y    = get_word();
  661.   if ((Ggif.image.size.x < 1) || (Ggif.image.size.y < 1)) {
  662.     longjmp(Ggif.abortion, FAIL_IMAGE_DIMENSIONS);
  663.   }
  664.   flags = get_byte();
  665.   Ggif.image.palette_bpp = (flags & 0x07) + 1;
  666.   Ggif.image.palette_sorted_p = ((flags & 0x20) != NONE);
  667.   Ggif.image.interlace_p = ((flags & 0x40) != NONE);
  668.   Ggif.image.palette_p = ((flags & 0x80) != NONE);
  669.   if (Gargs.verbose > 0) {
  670.     fprintf(Glog, "   image @ (%d, %d), size (%d, %d), bpp= %d, interlaced= %s, palette= %s, sorted= %s\n",
  671.           Ggif.image.topleft.x, Ggif.image.topleft.y, Ggif.image.size.x, Ggif.image.size.y,
  672.           Ggif.image.palette_bpp, YESNO(Ggif.image.interlace_p), YESNO(Ggif.image.palette_p), YESNO(Ggif.image.palette_sorted_p));
  673.   }
  674.   i = 0;
  675.   if (Ggif.image.palette_p) {
  676.     /*
  677.      * global colour map
  678.      */
  679.     size = 1 << Ggif.image.palette_bpp;
  680.     for (; (i < size); i++) {
  681.       colour = get_byte() << 8;
  682.       colour |= get_byte() << 16;
  683.       colour |= get_byte() << 24;
  684.       Ggif.image.palette[i] = colour;
  685.     }
  686.   }
  687.   for (size = 1 << Ggif.screen.palette_bpp; (i < size); i++) {
  688.     Ggif.image.palette[i] = Ggif.screen.palette[i];
  689.   }
  690.   /*
  691.    * we now force the `transparent' entry to black so that does not dither!
  692.    */
  693.   if ((Ggif.control_p) && (Ggif.control.transparent_p)) {
  694.     Ggif.image.palette[Ggif.control.transparent] = os_COLOUR_BLACK;
  695.   }
  696. }
  697.  
  698.  
  699.  
  700. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  701.  * here the GIF file is expecting the initial minimum code size byte
  702.  * what we do is expand the stream into a 256 colour image
  703.  * where each row is Gsprite.pixel_size.x bytes across, starting at Gsprite.image.bitmap
  704.  * and each row is Gsprite.image.line_length bytes apart
  705.  */
  706.  
  707. static void gif_lzw_decode(void) {
  708.   register byte                 *sp;
  709.   register byte                 *bufptr;
  710.   const byte                    *bufend;
  711.   
  712.   int                           code, fc, oc;
  713.   int                           c, size, guard;
  714.  
  715.   /* Initialize for decoding a new image...
  716.    */
  717.   size = get_byte();
  718.   if ((size < 2) || (size > 9)) {
  719.     longjmp(Ggif.abortion, FAIL_CORRUPT_LZW);
  720.   }
  721.   lzw_init(size);
  722.  
  723.   /* Initialize in case they forgot to put in a clear code.
  724.    * (This shouldn't happen, but we'll try and decode it anyway...)
  725.    */
  726.   oc = fc = 0;
  727.  
  728.   /* Set up the Glzw.stack pointer and decode buffer pointer
  729.    */
  730.   sp = Glzw.stack;
  731.   bufptr = gif_row(); bufend = bufptr + Gsprite.pixel_size.x;
  732.  
  733.   /* This is the main loop.  For each code we get we pass through the
  734.    * linked list of Glzw.prefix codes, pushing the corresponding "character" for
  735.    * each code onto the Glzw.stack.  When the list reaches a single "character"
  736.    * we push that on the Glzw.stack too, and then start unGlzw.stacking each
  737.    * character for output in the correct order.  Special handling is
  738.    * included for the Glzw.clear code, and the whole thing ends when we get
  739.    * an Glzw.ending code.
  740.    */
  741.   while (c = lzw_get_next_code(), (c != Glzw.ending)) {
  742.     #if DEBUG_LZW
  743.       fprintf(stderr, "LZW(%#x)\n", c);
  744.     #endif /* DEBUG_LZW */
  745.     if (c == Glzw.clear) {
  746.       #if DEBUG_LZW
  747.         fprintf(stderr, "LZW_CLEAR(%#x)\n", c);
  748.       #endif /* DEBUG_LZW */
  749.       /*
  750.        * If the code is a Glzw.clear code, reinitialize all necessary items.
  751.        */
  752.       Glzw.current_size = size + 1;
  753.       Glzw.slot = Glzw.newcodes;
  754.       Glzw.top_slot = 1 << Glzw.current_size;
  755.  
  756.       #ifndef NDEBUG
  757.         /*
  758.          * help spot bugs/errors quicker
  759.          */
  760.         memset(Glzw.prefix, 0xee, sizeof(Glzw.prefix));
  761.       #endif /* NDEBUG */
  762.  
  763.       /* Continue reading codes until we get a non-Glzw.clear code
  764.        * (Another unlikely, but possible case...)
  765.        */
  766.       while ((c = lzw_get_next_code()) == Glzw.clear) {
  767.         /* do nothing */
  768.       }
  769.       #if DEBUG_LZW
  770.         fprintf(stderr, "LZW_POST_CLEAR(%#x)\n", c);
  771.       #endif /* DEBUG_LZW */
  772.  
  773.       /* If we get an Glzw.ending code immediately after a Glzw.clear code
  774.        * (Yet another unlikely case), then break out of the loop.
  775.        */
  776.       if (c == Glzw.ending) {
  777.         break;
  778.       }
  779.  
  780.       /* Finally, if the code is beyond the range of already set codes,
  781.        * (This one had better NOT happen...  I have no idea what will
  782.        * result from this, but I doubt it will look good...) then set it
  783.        * to color zero.
  784.        */
  785.       if (c >= Glzw.slot) {
  786.         c = 0;
  787.       }
  788.  
  789.       if (c >= 0x100) {
  790.         /*
  791.          * a corrupt file
  792.          */
  793.         if (!Gargs.quiet_p) {
  794.           fprintf(stderr, "gif2rpc: %s: lzw-code %d too large\n", Gargs.gif, c);
  795.         }
  796.         if (Gargs.fussy_p) {
  797.           longjmp(Ggif.abortion, FAIL_CORRUPT_LZW);
  798.         }
  799.         break;
  800.       }
  801.  
  802.       assert(c >= 0);
  803.       assert(c < 0x100);
  804.  
  805.       oc = fc = c;
  806.  
  807.       /* And let us not forget to put the char into the buffer... And
  808.        * if, on the off chance, we were exactly one pixel from the end
  809.        * of the line, we have to send the buffer to the out_line()
  810.        * routine...
  811.        */
  812.       *bufptr++ = c;
  813.       if (bufptr == bufend) {
  814.         bufptr = gif_row(); bufend = bufptr + Gsprite.pixel_size.x;
  815.       }
  816.     } else {
  817.       /* In this case, it's not a clear code or an ending code, so
  818.        * it must be a code code...  So we can now decode the code into
  819.        * a stack of character codes. (clear as mud, right?)
  820.        */
  821.       code = c;
  822.  
  823.       /* Here we go again with one of those off chances...  If, on the
  824.        * off chance, the code we got is beyond the range of those already
  825.        * set up (Another thing which had better NOT happen...) we trick
  826.        * the decoder into thinking it actually got the last code read.
  827.        * (Hmmn... I'm not sure why this works...  But it does...)
  828.        */
  829.       if (code >= Glzw.slot) {
  830.         if (code > Glzw.slot) {
  831.           Glzw.bad_code_count++;
  832.         }
  833.         code = oc;
  834.         *sp++ = fc;
  835.         #if DEBUG_LZW
  836.           fprintf(stderr, "LZW_byteOC(%#x)\n", sp[-1]);
  837.         #endif /* DEBUG_LZW */
  838.       }
  839.  
  840.       /* Here we scan back along the linked list of prefixes, pushing
  841.        * helpless characters (ie. suffixes) onto the stack as we do so.
  842.        */
  843.       for (guard= LZW_MAX_CODES; ((code >= Glzw.newcodes) && (guard > 0)); guard--) {
  844.         assert(code >= 0);
  845.         assert(code < COUNT(Glzw.suffix));
  846.         *sp++ = Glzw.suffix[code];
  847.         #if DEBUG_LZW
  848.           fprintf(stderr, "LZW_byte(%#x)\n", sp[-1]);
  849.         #endif /* DEBUG_LZW */
  850.         code = Glzw.prefix[code];
  851.       }
  852.  
  853.       if (code >= 0x100) {
  854.         /*
  855.          * a corrupt file
  856.          */
  857.         if (!Gargs.quiet_p) {
  858.           fprintf(stderr, "gif2rpc: %s: lzw-code %d too large\n", Gargs.gif, code);
  859.         }
  860.         if (Gargs.fussy_p) {
  861.           longjmp(Ggif.abortion, FAIL_CORRUPT_LZW);
  862.         }
  863.         break;
  864.       }
  865.  
  866.       assert(code >= 0);
  867.       assert(code < 0x100);
  868.  
  869.       /* Push the last character on the Glzw.stack, and set up the new
  870.        * Glzw.prefix and Glzw.suffix, and if the required Glzw.slot number is greater
  871.        * than that allowed by the current bit size, increase the bit
  872.        * size.  (NOTE - If we are all full, we *don't* save the new
  873.        * Glzw.suffix and Glzw.prefix...  I'm not certain if this is correct...
  874.        * it might be more proper to overwrite the last code...
  875.        */
  876.       *sp++ = code;
  877.       #if DEBUG_LZW
  878.         fprintf(stderr, "LZW_byteCODE(%#x)\n", sp[-1]);
  879.       #endif /* DEBUG_LZW */
  880.       if (Glzw.slot < Glzw.top_slot) {
  881.         assert(Glzw.slot >= 0);
  882.         assert(Glzw.slot < 0x100);
  883.         Glzw.suffix[Glzw.slot] = fc = code;
  884.         Glzw.prefix[Glzw.slot++] = oc;
  885.         oc = c;
  886.       }
  887.       if (Glzw.slot >= Glzw.top_slot) {
  888.         if (Glzw.current_size < 12) {
  889.           Glzw.top_slot <<= 1;
  890.           Glzw.current_size++;
  891.         }
  892.       }
  893.  
  894.       /* Now that we've pushed the decoded string (in reverse order)
  895.        * onto the Glzw.stack, lets pop it off and put it into our decode
  896.        * buffer...  And when the decode buffer is full, write another
  897.        * line...
  898.        */
  899.       while (sp > Glzw.stack) {
  900.         *bufptr++ = *(--sp);
  901.         if (bufptr == bufend) {
  902.           bufptr = gif_row(); bufend = bufptr + Gsprite.pixel_size.x;
  903.         }
  904.       }
  905.     }
  906.   }
  907.   if ((bufptr != bufend) && ((bufend-bufptr) != Gsprite.pixel_size.x)) {
  908.     /*
  909.      * this probably indicates an error
  910.      * but do NOT fill it because some files seem to have an ``extra'' row!
  911.      */
  912.     if (!Gargs.quiet_p) {
  913.       fprintf(stderr, "gif2rpc: %s: missing %d pixels off last row\n", Gargs.gif, bufend - bufptr);
  914.     }
  915.     if (Gargs.fussy_p) {
  916.       longjmp(Ggif.abortion, FAIL_INCOMPLETE_LAST_ROW);
  917.     }
  918.   }
  919.   if (Glzw.block_count != 0) {
  920.     /*
  921.      * once again, this PROBABLY indicates an error
  922.      * but have seen sprites where an extra `0' byte exists
  923.      */
  924.     if (!Gargs.quiet_p) {
  925.       fprintf(stderr, "gif2rpc: %s: extra %d byte(s) at end of image\n", Gargs.gif, Glzw.block_count);
  926.     }
  927.     if (Gargs.fussy_p) {
  928.       longjmp(Ggif.abortion, FAIL_EXTRA_DATA_AFTER_IMAGE);
  929.     }
  930.   } else {
  931.     for (guard= Glzw.block_count; (guard); guard--) {
  932.       code = get_byte();
  933.     }
  934.   }
  935.   if (get_byte() != 0) {
  936.     if (Gargs.fussy_p) {
  937.       if (!Gargs.quiet_p) {
  938.         fprintf(stderr, "gif2rpc: %s: unexpected byte at end of image\n", Gargs.gif);
  939.       }
  940.       longjmp(Ggif.abortion, FAIL_BAD_EOF_IMAGE);
  941.     }
  942.   }
  943. }
  944.  
  945.  
  946.  
  947. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  948.  */
  949.  
  950. static void lzw_init(
  951.                 int     size) {
  952.   Glzw.current_size = size + 1;
  953.   Glzw.top_slot = 1 << Glzw.current_size;
  954.   Glzw.clear = 1 << size;
  955.   Glzw.ending = Glzw.clear + 1;
  956.   Glzw.slot = Glzw.newcodes = Glzw.ending + 1;
  957.   Glzw.nbits_left = 0;
  958.  
  959.   Glzw.row = 0;
  960.   Glzw.interlace_part = 0;
  961.   Glzw.block_count = 0;
  962.  
  963.   Glzw.real_row = 0;
  964. }
  965.  
  966.  
  967.  
  968. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  969.  */
  970.  
  971. static int lzw_get_next_code(void) {
  972.   unsigned int  ret;
  973.  
  974.   if (Glzw.nbits_left == 0) {
  975.     Glzw.b1 = get_image_byte();
  976.     Glzw.nbits_left = 8;
  977.   }
  978.  
  979.   ret = LSR(Glzw.b1,  (8 - Glzw.nbits_left));
  980.   while (Glzw.current_size > Glzw.nbits_left) {
  981.     Glzw.b1 = get_image_byte();
  982.     ret |= Glzw.b1 << Glzw.nbits_left;
  983.     Glzw.nbits_left += 8;
  984.   }
  985.   Glzw.nbits_left -= Glzw.current_size;
  986.   ret &= ~((~0) << Glzw.current_size);
  987.   return ret;
  988. }
  989.  
  990.  
  991.  
  992. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  993.  */
  994.  
  995. static byte *gif_row(void) {
  996.   int   row;
  997.   static const int Ggif_interlace_offset[4] = {0, 4, 2, 1}; /* The way Interlaced image should. */
  998.   static const int Ggif_interlace_jumps[4] =  {8, 8, 4, 2};    /* be read - offsets and jumps... */
  999.  
  1000.   #if DEBUG_LZW
  1001.     fprintf(stderr, "gif_row()\n");
  1002.   #endif /* DEBUG_LZW */
  1003.   if (Gargs.hourglass_p) {
  1004.     row = Glzw.real_row++;
  1005.     xhourglass_percentage((row * 100) / Gsprite.pixel_size.y);
  1006.   }
  1007.   row = Glzw.row++;
  1008.   if (Ggif.image.interlace_p) {
  1009.     assert(Glzw.interlace_part < 4);
  1010.     row *= Ggif_interlace_jumps[Glzw.interlace_part];
  1011.     row += Ggif_interlace_offset[Glzw.interlace_part];
  1012.     if (row >= Ggif.image.size.y) {
  1013.       Glzw.interlace_part++;
  1014.       if (Glzw.interlace_part < 4) {
  1015.         row = Ggif_interlace_offset[Glzw.interlace_part];
  1016.         Glzw.row = 1;
  1017.       }
  1018.     }
  1019.   }
  1020.   if (Gargs.verbose > 1) {
  1021.     fprintf(Glog, "     processing row %d @ %#x in file\n", row, Ggif.file_pos);
  1022.   }
  1023.   row = MIN(row, Ggif.image.size.y - 1);
  1024.   return &Gsprite.image.bitmap[row * Gsprite.image.line_length];
  1025. }
  1026.  
  1027.  
  1028.  
  1029. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1030.  */
  1031.  
  1032. static void gif_skip_data(void) {
  1033.   int           size;
  1034.   byte          dummy;
  1035.  
  1036.   for (; (size = get_byte(), (size > 0));) {
  1037.     do {
  1038.       dummy = get_byte();
  1039.       size--;
  1040.     } while (size != 0);
  1041.   }
  1042. }
  1043.  
  1044.  
  1045.  
  1046. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1047.  */
  1048.  
  1049. static byte get_image_byte(void) {
  1050.   if (Glzw.block_count == 0) {
  1051.     return get_image_byte_aux();
  1052.   }
  1053.   Glzw.block_count--;
  1054.   return get_byte();
  1055. }
  1056.  
  1057.  
  1058.  
  1059. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1060.  */
  1061.  
  1062. static byte get_image_byte_aux(void) {
  1063.   Glzw.block_count = get_byte();
  1064.   return get_image_byte();
  1065. }
  1066.  
  1067.  
  1068.  
  1069. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1070.  */
  1071.  
  1072. static byte get_byte(void) {
  1073.   if (Ggif.file_pos >= Ggif.file_siz) {
  1074.     return fail();
  1075.   }
  1076.   return Ggif.file_buffer[Ggif.file_pos++];
  1077. }
  1078.  
  1079.  
  1080.  
  1081. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1082.  */
  1083.  
  1084. static int get_word(void) {
  1085.   unsigned int   i;
  1086.  
  1087.   i = get_byte();
  1088.   return i | (get_byte() << 8);
  1089. }
  1090.  
  1091.  
  1092.  
  1093. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1094.  */
  1095.  
  1096. static byte fail(void) {
  1097.   longjmp(Ggif.abortion, FAIL_SHORT_FILE);
  1098.   return '\0';
  1099. }
  1100.  
  1101.  
  1102.  
  1103. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1104.  * this should be called AFTER know gif image size and palette
  1105.  * it sets up the Gsprite variable
  1106.  */
  1107.  
  1108. static void prepare_sprite(void) {
  1109.   int                   aspect_ratio;
  1110.   bits                  flags;
  1111.   int                   lb_bpp;
  1112.   os_mode               mode;
  1113.   size_t                area_size;
  1114.   char                  *name;
  1115.   int                   i;
  1116.   osspriteop_header     *sprite;
  1117.   char                  name_buffer[osspriteop_NAME_LIMIT + 1];
  1118.   os_error              *error;
  1119.   int                   palette_size;
  1120.  
  1121.   Gsprite.pixel_size = Ggif.image.size;           /* structure copy */
  1122.   assert(Gsprite.pixel_size.x > 0);
  1123.   assert(Gsprite.pixel_size.y > 0);
  1124.   aspect_ratio = 15 + ((Ggif.screen.aspect_ratio) ? Ggif.screen.aspect_ratio : 49);
  1125.   if (aspect_ratio != 64) {
  1126.     if (Gargs.verbose > 0) {
  1127.       fprintf(Glog, "   aspect ratio is %d/64\n", aspect_ratio);
  1128.     }
  1129.     if (Gargs.square_p) {
  1130.       aspect_ratio = 64;
  1131.     }
  1132.   }
  1133.   Gsprite.dpi.x = (90 * aspect_ratio) / 64;
  1134.   Gsprite.dpi.y = 90;
  1135.   Gsprite.transparent_p = ((Ggif.control_p) ? (Ggif.control.transparent_p) : FALSE);
  1136.   
  1137.   palette_size = 0;
  1138.   switch (Gargs.bpp) {
  1139.   case -1:
  1140.     flags = os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_LOG2_BPP, &lb_bpp);
  1141.     if ((flags & _C) != NONE) {
  1142.       if (!Gargs.quiet_p) {
  1143.         fprintf(stderr, "gif2rpc: failed to find screen mode\n");
  1144.       }
  1145.       exit(EXIT_FAILURE);
  1146.     }
  1147.     if (lb_bpp <= 3) {
  1148.       error = xcolourtrans_read_palette(
  1149.                 (osspriteop_area const *)colourtrans_CURRENT_MODE,
  1150.                 (osspriteop_id)colourtrans_CURRENT_PALETTE,
  1151.                 (os_palette *)Goutput_palette,
  1152.                 sizeof(Goutput_palette),
  1153.                 NONE,
  1154.                 NULL);
  1155.       if (error) {
  1156.         if (!Gargs.quiet_p) {
  1157.           fprintf(stderr, "gif2rpc: unable to read palette (%#x): '%s'\n", error->errnum, error->errmess);
  1158.         }
  1159.         exit(EXIT_FAILURE);
  1160.       }
  1161.       palette_size = sizeof(os_colour_pair) * (1 << (1 << lb_bpp));
  1162.     }
  1163.     break;
  1164.   case 0:
  1165.     lb_bpp = 3;
  1166.     palette_size = sizeof(os_colour_pair) * 256;                /* always 256 entries now */
  1167.     if (Ggif.input_bpp < 1) {
  1168.       assert(!"internal error");
  1169.     } else if (Ggif.input_bpp < 2) {
  1170.       lb_bpp = 0;
  1171.     } else if (Ggif.input_bpp < 3) {
  1172.       lb_bpp = 1;
  1173.     } else if (Ggif.input_bpp < 5) {
  1174.       lb_bpp = 2;
  1175.     }
  1176.     break;
  1177.   case 1:       lb_bpp = 0;     break;
  1178.   case 2:       lb_bpp = 1;     break;
  1179.   case 4:       lb_bpp = 2;     break;
  1180.   case 8:       lb_bpp = 3;     break;
  1181.   case 16:      lb_bpp = 4;     break;
  1182.   case 32:      lb_bpp = 5;     break;
  1183.   default:
  1184.     if (!Gargs.quiet_p) {
  1185.       fprintf(stderr, "gif2rpc: invalid command line: bpp value %d, should be one of {0,1,2,4,8,16,32}\n", Gargs.bpp);
  1186.     }
  1187.     exit(EXIT_FAILURE);
  1188.   }
  1189.   if ((lb_bpp <= 3) && (palette_size == 0)) {
  1190.     default_wimppalette(Goutput_palette, lb_bpp);
  1191.   }
  1192.   Ggif.output_lb_bpp = lb_bpp;
  1193.  
  1194.   /*
  1195.    * we will generate a 256 colour sprite, and then downgrade it if necessary
  1196.    */
  1197.   lb_bpp = MAX(3, lb_bpp);
  1198.  
  1199.   Gsprite.lb_bpp = lb_bpp;
  1200.  
  1201.   if (Gargs.name == NULL) {
  1202.     if (Gargs.autoname_p) {
  1203.       int         len;
  1204.       
  1205.       name = strrchr(Gargs.gif, '.');
  1206.       if (!name) {
  1207.         name = strrchr(Gargs.gif, ':');
  1208.       }
  1209.       if (!name) {
  1210.         name = Gargs.gif;
  1211.       } else {
  1212.         name++;
  1213.       }
  1214.       len = strlen(name);
  1215.       if (len > osspriteop_NAME_LIMIT) {
  1216.         name += len - osspriteop_NAME_LIMIT;
  1217.       }
  1218.       name = strcpy(name_buffer, name);
  1219.     } else {
  1220.       if (sizeof(SPRITE_NAME) >= sizeof(name_buffer)) {
  1221.         abort();
  1222.       }
  1223.       name = strcpy(name_buffer, SPRITE_NAME);
  1224.     }
  1225.   } else {
  1226.     name = Gargs.name;
  1227.   }
  1228.   for (i= 0; (i < osspriteop_NAME_LIMIT); i++) {
  1229.     if ((name[i] <= 32) || (name[i] == 0x7f)) {
  1230.       break;
  1231.     }
  1232.     name[i] = (char)tolower(name[i]);
  1233.   }
  1234.   name[i] = '\0';
  1235.  
  1236.   mode = (lb_bpp == 3) ? os_MODE8BPP90X90 : MODE(lb_bpp, Gsprite.dpi.x, Gsprite.dpi.y);
  1237.  
  1238.   Gsprite.image.line_length = 4 * WORD_WIDTH(Gsprite.pixel_size.x * (1 << lb_bpp));
  1239.   Gsprite.mask.line_length = 4 * WORD_WIDTH(Gsprite.pixel_size.x * ((lb_bpp <= 3) ? (1 << lb_bpp) : 1));
  1240.   /*
  1241.    * get a sprite area big enough
  1242.    */
  1243.   area_size = sizeof(osspriteop_area)
  1244.                 + sizeof (osspriteop_header)
  1245.                 + palette_size
  1246.                 + (Gsprite.image.line_length * Gsprite.pixel_size.y)
  1247.                 + ((Gsprite.transparent_p)
  1248.                    ? (Gsprite.mask.line_length * Gsprite.pixel_size.y)
  1249.                    : 0);
  1250.  
  1251.   Gsprite.area = malloc(area_size);
  1252.   if (!Gsprite.area) {
  1253.     if (!Gargs.quiet_p) {
  1254.       fprintf(stderr, "gif2rpc: out of memory (need %u)\n", area_size);
  1255.     }
  1256.     exit(EXIT_FAILURE);
  1257.   }
  1258.  
  1259.   Gsprite.area->size  = area_size;
  1260.   Gsprite.area->first = sizeof(osspriteop_area);
  1261.   osspriteop_clear_sprites(osspriteop_USER_AREA, Gsprite.area);
  1262.   osspriteop_create_sprite(osspriteop_NAME, Gsprite.area, name, FALSE, Gsprite.pixel_size.x, Gsprite.pixel_size.y, mode);
  1263.   sprite = osspriteop_select_sprite(osspriteop_NAME, Gsprite.area, (osspriteop_id)name);
  1264.   assert(sprite);
  1265.   Gsprite.sprite = sprite;
  1266.   if (Gsprite.transparent_p) {
  1267.     osspriteop_create_mask(osspriteop_PTR, Gsprite.area, (osspriteop_id)sprite);
  1268.   }
  1269.   if (palette_size != 0) {
  1270.     int                 i;
  1271.     os_colour_pair      *palette = (os_colour_pair *)(sprite+1);
  1272.     const os_colour     *in;
  1273.  
  1274.     insert_bytes_before_image(Gsprite.area, sprite, palette_size);
  1275.     /*
  1276.      * manually insert palette
  1277.      */
  1278.     in = (Gargs.bpp == 0) ? Ggif.image.palette : Goutput_palette;
  1279.     for (i= 0; (i < (palette_size / sizeof(os_colour_pair))); i++) {
  1280.       palette[i].on = palette[i].off = in[i];
  1281.     }
  1282.   }
  1283.   Gsprite.image.bitmap = ((byte *)sprite) + sprite->image;
  1284.   Gsprite.mask.bitmap = ((byte *)sprite) + sprite->mask;
  1285. }
  1286.  
  1287.  
  1288.  
  1289. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1290.  */
  1291.  
  1292. static void insert_bytes_before_image(
  1293.                 osspriteop_area         *area,
  1294.                 osspriteop_header       *sprite,
  1295.                 int                     siz) {
  1296.   assert(area);
  1297.   assert(sprite);
  1298.   assert(siz >= 0);
  1299.  
  1300.   area->used    += siz;
  1301.   assert(area->used <= area->size);
  1302.  
  1303.   sprite->size  += siz;
  1304.   sprite->image += siz;
  1305.   sprite->mask  += siz;
  1306. }
  1307.  
  1308.  
  1309.  
  1310. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1311.  */
  1312.  
  1313. static void load_file(void) {
  1314.   int   type;
  1315.   bits  file_type;
  1316.   int   siz;
  1317.  
  1318.   if (Gargs.verbose > 0) {
  1319.     fprintf(Glog, "processing GIF file `%s'\n", Gargs.gif);
  1320.   }
  1321.   type = osfile_read_stamped(Gargs.gif, NULL, NULL, (int *)&Ggif.file_siz, NULL, &file_type);
  1322.   if (type != osfile_IS_FILE) {
  1323.     if (!Gargs.quiet_p) {
  1324.       fprintf(stderr, "gif2rpc: %s: this is not a (GIF) file\n", Gargs.gif);
  1325.     }
  1326.     exit(EXIT_FAILURE);
  1327.   }
  1328.   if (Gargs.fussy_p) {
  1329.     if (file_type != 0xff0) {
  1330.       if (!Gargs.quiet_p) {
  1331.         fprintf(stderr, "gif2rpc: %s: this is not typed as a GIF file\n", Gargs.gif);
  1332.       }
  1333.       exit(EXIT_FAILURE);
  1334.     }
  1335.   }
  1336.   if (Gargs.verbose > 1) {
  1337.     fprintf(Glog, "   GIF file is %#x bytes long\n", Ggif.file_siz);
  1338.   }
  1339.   Ggif.file_buffer = malloc(MAX(1, Ggif.file_siz));
  1340.   if (!Ggif.file_buffer) {
  1341.     if (!Gargs.quiet_p) {
  1342.       fprintf(stderr, "gif2rpc: out of memory (need %u)\n", Ggif.file_siz);
  1343.     }
  1344.     exit(EXIT_FAILURE);
  1345.   }
  1346.   osfile_load_stamped(Gargs.gif, (byte *)Ggif.file_buffer, NULL, NULL, &siz, NULL);
  1347.   if (siz != Ggif.file_siz) {
  1348.     if (!Gargs.quiet_p) {
  1349.       fprintf(stderr, "gif2rpc: %s: cant read file\n", Gargs.gif);
  1350.     }
  1351.     exit(EXIT_FAILURE);
  1352.   }
  1353. }
  1354.  
  1355.  
  1356.  
  1357. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1358.  */
  1359.  
  1360. static void turn_off_hourglass(void) {
  1361.   xhourglass_off();
  1362. }
  1363.  
  1364.  
  1365.  
  1366. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1367.  */
  1368.  
  1369. static void process_mask(void) {
  1370.   const byte    *source;
  1371.   byte          *dest;
  1372.   int           x, y;
  1373.   byte          transparent;
  1374.   int           width;
  1375.   unsigned int  b;
  1376.  
  1377.   source = Gsprite.image.bitmap;
  1378.   dest = Gsprite.mask.bitmap;
  1379.   assert(dest);
  1380.   transparent = Ggif.control.transparent;
  1381.   width = Gsprite.pixel_size.x;
  1382.  
  1383.   switch (Gsprite.lb_bpp) {
  1384.   case 3:
  1385.     /*
  1386.      * create an 8bpp mask
  1387.      */
  1388.     if (Gargs.verbose > 0) {
  1389.       fprintf(Glog, "  created 8bpp transparency mask\n");
  1390.     }
  1391.     for (y= Gsprite.pixel_size.y; (y > 0); y--) {
  1392.       for (x=  width-1; (x >= 0); x--) {
  1393.         dest[x] = (source[x] == transparent) ? 0x00 : 0xff;
  1394.       }
  1395.       source += Gsprite.image.line_length;
  1396.       dest += Gsprite.mask.line_length;
  1397.     }
  1398.     break;
  1399.   case 4:
  1400.   case 5:
  1401.     /*
  1402.      * create a 1bpp mask
  1403.      */
  1404.     if (Gargs.verbose > 0) {
  1405.       fprintf(Glog, "  created 1bpp transparency mask\n");
  1406.     }
  1407.     for (y= Gsprite.pixel_size.y; (y > 0); y--) {
  1408.       b = ~0u;
  1409.       for (x= 0; (x < width);) {
  1410.         if (source[x] == transparent) {
  1411.           b &= ~(1u << (x & 0x1f));
  1412.         }
  1413.         x++;
  1414.         if ((x & 0x1f) == 0) {
  1415.           *(((unsigned int *)dest) + LSR(x, 5) - 1) = b;
  1416.           b = ~0u;
  1417.         }
  1418.       }
  1419.       if (x & 0x1f) {
  1420.         *(((unsigned int *)dest) + LSR(x, 5)) = b;
  1421.       }
  1422.       source += Gsprite.image.line_length;
  1423.       dest += Gsprite.mask.line_length;
  1424.     }
  1425.     break;
  1426.   default:
  1427.     assert(!"internal error");
  1428.     longjmp(Ggif.abortion, FAIL_INTERNAL_PROCESS_MASK);
  1429.   }
  1430. }
  1431.  
  1432.  
  1433.  
  1434. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1435.  */
  1436.  
  1437. static void process_image(void) {
  1438.   int                   i;
  1439.   process_gif_fn        fn;
  1440.  
  1441.   Gpg.buffer                    = Gsprite.image.bitmap;
  1442.   Gpg.pixel_width               = Gsprite.pixel_size.x;
  1443.   Gpg.pixel_height              = Gsprite.pixel_size.y;
  1444.   Gpg.line_length               = Gsprite.image.line_length;
  1445.   Gpg.in_palette.colours        = Ggif.image.palette;
  1446.   Gpg.in_palette.ncolours       = 1 << Ggif.input_bpp;
  1447.   switch (Gsprite.lb_bpp) {
  1448.   case 3:
  1449.     /*
  1450.      * this is a special case
  1451.      */
  1452.     if (Gargs.bpp == 0) {
  1453.       /*
  1454.        * we want to preserve as much info as possible of the GIF
  1455.        * but if can reduce bpp then might as well:
  1456.        */
  1457.       if (Ggif.output_lb_bpp < 3) {
  1458.         reduce_sprite_lb_bpp(Ggif.output_lb_bpp);
  1459.       }
  1460.       return;
  1461.     }
  1462.     Gpg.fn = map_scaled_rgb_to_palette_index;
  1463.     if ((!Gargs.accurate_p) && (Ggif.output_lb_bpp == 3) && (Ggif.input_bpp != 0)) {
  1464.       Gpg.fn = map_scaled_rgb_to_8bpp_colour_number_quick;
  1465.     }
  1466.  
  1467.     Gpg.out_palette.ncolours      = 1 << (1 << Ggif.output_lb_bpp);
  1468.     Gpg.out_palette.colours       = malloc(sizeof(*Gpg.out_palette.colours) * Gpg.out_palette.ncolours);
  1469.     if (!Gpg.out_palette.colours) {
  1470.       if (!Gargs.quiet_p) {
  1471.         fprintf(stderr, "gif2rpc: %s: out of memory\n", Gargs.gif);
  1472.       }
  1473.       return;
  1474.     }
  1475.     {
  1476.       int               index;
  1477.       const os_colour   *palette;
  1478.       rgbtuple          *tuples;
  1479.       os_colour         colour;
  1480.       int               red, grn, blu;
  1481.  
  1482.       palette = Goutput_palette;
  1483.       tuples = Gpg.out_palette.colours;
  1484.       for (index= Gpg.out_palette.ncolours - 1; (index >= 0); index--) {
  1485.         colour = palette[index];
  1486.         red = (colour >> (1 * 8)) & 0xff; red |= red << 8;
  1487.         grn = (colour >> (2 * 8)) & 0xff; grn |= grn << 8;
  1488.         blu = (colour >> (3 * 8)) & 0xff; blu |= blu << 8;
  1489.         tuples[index].blu = blu;
  1490.         tuples[index].grn = grn;
  1491.         tuples[index].red = red;
  1492.       }
  1493.     }
  1494.     break;
  1495.   case 4:
  1496.     Gpg.fn = (Gargs.accurate_p)
  1497.              ? map_scaled_rgb_to_16bpp_colour_accurate
  1498.              : map_scaled_rgb_to_16bpp_colour_quick;
  1499.     break;
  1500.   case 5:
  1501.     Gpg.fn = NULL;
  1502.     break;
  1503.   default:
  1504.     assert(!"internal error");
  1505.   }
  1506.  
  1507.   if (!Gargs.filter) {
  1508.     i = 1;                            /* 2x2 dither */
  1509.   } else {
  1510.     for (i= 0; (i < COUNT(Gfilters)); i++) {
  1511.       if (stricmp(Gfilters[i].name, Gargs.filter) == 0) {
  1512.         break;
  1513.       }
  1514.     }
  1515.     if (i == COUNT(Gfilters)) {
  1516.       if (!Gargs.quiet_p) {
  1517.         fprintf(stderr, "gif2rpc: unrecognised filter `%s' specified on command line\n",
  1518.                         Gargs.filter);
  1519.       }
  1520.       if (Gargs.fussy_p) {
  1521.         exit(EXIT_FAILURE);
  1522.       }
  1523.       i = 1;                            /* 2x2 dither */
  1524.     }
  1525.   }
  1526.   assert(i >= 0);
  1527.   assert(i < COUNT(Gfilters));
  1528.  
  1529.   Gsprite.filter = i;
  1530.  
  1531.   if (Gargs.verbose > 0) {
  1532.     fprintf(Glog,
  1533.                 "  %s conversion to %dbpp\n",
  1534.                 Gfilters[Gsprite.filter].name, 1 << Gsprite.lb_bpp);
  1535.   }
  1536.  
  1537.   fn = filter_fn(Gsprite.filter);
  1538.   if (!fn) {
  1539.     if (!Gargs.quiet_p) {
  1540.       fprintf(stderr,
  1541.                 "gif2rpc: %s: %s conversion to %dbpp not implemented, using 2x2 dither\n",
  1542.                 Gargs.gif, Gfilters[i].name, 1 << Gsprite.lb_bpp);
  1543.     }
  1544.     /*
  1545.      * default to 2x2 dither if not implemented others yet
  1546.      */
  1547.     Gsprite.filter = 1;
  1548.     fn = filter_fn(Gsprite.filter);
  1549.     assert(fn);
  1550.   }
  1551.   if ((*fn)(&Gpg)) {
  1552.     /*
  1553.      * oh dear, out of memory
  1554.      */
  1555.     if (!Gargs.quiet_p) {
  1556.       fprintf(stderr,
  1557.                 "gif2rpc: %s: out of memory using %s filter\n",
  1558.                 Gargs.gif, Gfilters[Gsprite.filter].name);
  1559.     }
  1560.     if (Gargs.fussy_p) {
  1561.       exit(EXIT_FAILURE);
  1562.     }
  1563.     if (Gsprite.filter > 1) {
  1564.       /*
  1565.        * default to 2x2 dither (uses no memory)
  1566.        */
  1567.       Gsprite.filter = 1;
  1568.       fn = filter_fn(Gsprite.filter);
  1569.       assert(fn);
  1570.       (*fn)(&Gpg);
  1571.     }
  1572.   }
  1573.   if ((Gsprite.lb_bpp == 3)
  1574.       && (Ggif.output_lb_bpp < 3)) {
  1575.     reduce_sprite_lb_bpp(Ggif.output_lb_bpp);
  1576.   }
  1577. }
  1578.  
  1579.  
  1580.  
  1581. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1582.  */
  1583.  
  1584.  
  1585. static process_gif_fn filter_fn(
  1586.                 int     index) {
  1587.   process_gif_fn        fn = NULL;
  1588.  
  1589.   assert(index >= 0);
  1590.   assert(index < COUNT(Gfilters));
  1591.  
  1592.   switch (Gsprite.lb_bpp) {
  1593.   case 3:
  1594.     fn = Gfilters[index].fn_8bpp;
  1595.     break;
  1596.  
  1597.   case 4:
  1598.     fn = (Gargs.force_66_p)
  1599.          ? Gfilters[index].fn_16bpp_66bit
  1600.          : Gfilters[index].fn_16bpp_48bit;
  1601.     break;
  1602.  
  1603.   case 5:
  1604.     fn = process_gif_32bpp;
  1605.     break;
  1606.  
  1607.   default:
  1608.     assert(!"internal error");
  1609.     longjmp(Ggif.abortion, FAIL_INTERNAL_PROCESS_IMAGE);
  1610.   }
  1611.   return fn;
  1612. }
  1613.  
  1614. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1615.  * this unconditionally converts an 8bpp image into either a 8bpp, 4bpp, 2bpp, or 1bpp image
  1616.  * note that converting to 8bpp is useful if the source is a 16bpp image...
  1617.  * note that the original *must* have a 256 entry palette attached
  1618.  */
  1619.  
  1620. static void reduce_sprite_lb_bpp(
  1621.                 int             lb_bpp) {
  1622.   int                   line_length;
  1623.   int                   height;
  1624.   byte                  *rove;
  1625.   int                   original_size;
  1626.   int                   ppw;
  1627.   int                   bpp;
  1628.   reducebitmap          rb;
  1629.   osspriteop_header     *sprite = Gsprite.sprite;
  1630.   static const os_mode  modes[4] = {
  1631.                                 os_MODE1BPP90X90, os_MODE2BPP90X90,
  1632.                                 os_MODE4BPP90X90, os_MODE8BPP90X90};
  1633.  
  1634.   assert(sprite);
  1635.   assert(lb_bpp >= 0);
  1636.   assert(lb_bpp <= 3);
  1637.  
  1638.   Gsprite.lb_bpp = lb_bpp;
  1639.   sprite->mode = modes[lb_bpp];
  1640.   bpp = 1 << lb_bpp;
  1641.   if (Gargs.verbose > 0) {
  1642.     fprintf(Glog, "  reducing image to %d bpp\n", bpp);
  1643.   }
  1644.   /*
  1645.    * the LEGAL way to do this is to create a sprite of the required size,
  1646.    * switch output to it, and then render our 256 colour sprite into it
  1647.    * but fuck that, let's just move that memory about ourselves, it is
  1648.    * easier
  1649.    */
  1650.   line_length = 4 * WORD_WIDTH(Gsprite.pixel_size.x * bpp);
  1651.   height = Gsprite.pixel_size.y;
  1652.   rove = (byte *)(sprite + 1);                          /* start of palette */
  1653.   if (sprite->image == sizeof(osspriteop_header)) {
  1654.     /*
  1655.      * no palette, so must be using default palette!
  1656.      */
  1657.     if (Gargs.verbose > 0) {
  1658.       fprintf(Glog, "  the reduced image uses the default palette\n");
  1659.     }
  1660.   } else {
  1661.     /*
  1662.      * check original palette was wide enough!
  1663.      */
  1664.     assert(sprite->image >= (((1 << bpp) * sizeof(os_colour_pair)) + sizeof(osspriteop_header)));
  1665.     rove += (1 << bpp) * sizeof(os_colour_pair);          /* end of palette */
  1666.     if (Gargs.verbose > 0) {
  1667.       fprintf(Glog, "  the reduced image has a palette\n");
  1668.     }
  1669.   }
  1670.   sprite->width = WORD_WIDTH(Gsprite.pixel_size.x * bpp) - 1;           /* must do this! */
  1671.   ppw = 32 / bpp;
  1672.   sprite->right_bit = 31 - (bpp * (Gsprite.pixel_size.x % ppw));        /* and must do this! */
  1673.   sprite->image = rove - ((byte *)sprite);              /* offset of (new) image */
  1674.   rb.source.bitmap      = Gsprite.image.bitmap;
  1675.   rb.source.line_length = Gsprite.image.line_length;
  1676.   rb.dest.bitmap        = rove;
  1677.   rb.dest.line_length   = line_length;
  1678.   rb.bpp                = bpp;
  1679.   rb.pixel_size         = Gsprite.pixel_size;           /* structure copy */
  1680.   reduce_bitmap(&rb);                                   /* process image */
  1681.   Gsprite.image.bitmap = rove;                          /* set up incase re-process! */
  1682.   rove += line_length * height;                         /* end of image */
  1683.   if (Gsprite.transparent_p) {
  1684.     if (Gargs.verbose > 0) {
  1685.       fprintf(Glog, "  reducing transparency to %d bpp\n", bpp);
  1686.     }
  1687.     assert(sprite->image <= sprite->mask);              /* we make this assumption */
  1688.     assert(Gsprite.mask.line_length == Gsprite.image.line_length);      /* I should hope so! */
  1689.     assert(Gsprite.mask.bitmap);
  1690.     sprite->mask = rove - ((byte *)sprite);             /* offset of transparency */
  1691.     rb.source.bitmap      = Gsprite.mask.bitmap;
  1692.     rb.dest.bitmap        = rove;
  1693.     reduce_bitmap(&rb);                                 /* process transparency */
  1694.     Gsprite.mask.bitmap = rove;                         /* set up incase re-process! */
  1695.     Gsprite.mask.line_length = line_length;             /* set up incase re-process! */
  1696.     rove += line_length * height;                       /* end of transparency */
  1697.   } else {
  1698.     sprite->mask = sprite->image;
  1699.   }
  1700.   Gsprite.image.line_length = line_length;              /* set up incase re-process! */
  1701.   original_size = sprite->size;
  1702.   sprite->size = rove - ((byte *)sprite);
  1703.   Gsprite.area->used -= original_size - sprite->size;
  1704. }
  1705.  
  1706.  
  1707.  
  1708. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1709.  * this takes a source image of resolution 8bpp
  1710.  * and converts it to a destination image of resolution rb->bpp (8/4/2/1)
  1711.  * it will use the lower rb->bpp of each source byte
  1712.  * note can overlap, so long as if dest <= source
  1713.  */
  1714.  
  1715. static void reduce_bitmap(
  1716.                 const reducebitmap      *rb) {
  1717.   int   x, y;
  1718.   int   pixel_width, word_width, bit_width;
  1719.   int   bpp;
  1720.   int   bit;
  1721.   bits  w, mask;
  1722.   byte  *source;
  1723.   bits  *dest;
  1724.   int   ppw;
  1725.   int   ds, dd;
  1726.  
  1727.  
  1728.   assert(rb);
  1729.   assert(rb->source.bitmap);
  1730.   assert(rb->dest.bitmap);
  1731.   assert(rb->dest.line_length > 0);
  1732.   assert((rb->dest.line_length % 4) == 0);      /* since we are writing words at a time */
  1733.   assert((rb->bpp == 8) || (rb->bpp == 4) || (rb->bpp == 2) || (rb->bpp == 1));
  1734.  
  1735.  
  1736.   bpp = rb->bpp;
  1737.   bit_width = rb->pixel_size.x * bpp;
  1738.   word_width = WORD_WIDTH(bit_width);
  1739.   assert((word_width * 4) <= ABS(rb->dest.line_length));
  1740.   assert(ABS(rb->source.line_length) >= ABS(rb->dest.line_length));
  1741.   ppw = 32 / bpp;
  1742.   pixel_width = word_width * ppw;
  1743.   assert(pixel_width >= rb->pixel_size.x);
  1744.   assert(pixel_width < (rb->pixel_size.x+ppw));
  1745.   source = rb->source.bitmap;
  1746.   dest = (bits *)rb->dest.bitmap;
  1747.   mask = ~(~0u << bpp);
  1748.   ds = rb->source.line_length - pixel_width;
  1749.   dd = LSR(rb->dest.line_length, 2) - word_width;
  1750.   for (y= rb->pixel_size.y; (y > 0); y--) {
  1751.     /*
  1752.      * go left to right because may have source == dest
  1753.      */
  1754.     assert(source == (rb->source.bitmap + ((rb->pixel_size.y - y) * rb->source.line_length)));
  1755.     assert(dest   == (bits *)(rb->dest.bitmap   + ((rb->pixel_size.y - y) * rb->dest.line_length)));
  1756.     x= pixel_width;
  1757.     do {
  1758.       w = 0;
  1759.       bit = 0;
  1760.       do {
  1761.         w |= (mask & *source++) << bit;         /* mask to handle transparency layer */
  1762.         bit += bpp;
  1763.       } while (bit < 32);
  1764.       *dest++ = w;
  1765.       x -= ppw;
  1766.     } while (x > 0);
  1767.     assert(x == 0);
  1768.     source += ds;
  1769.     dest += dd;
  1770.   }
  1771. }
  1772.  
  1773.  
  1774.  
  1775. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1776.  */
  1777.  
  1778. static bool process_gif_32bpp(
  1779.                 const process_gif       *p) {
  1780.   const byte            *source;
  1781.   bits                  *dest;
  1782.   int                   x, y, width, line_length;
  1783.   const os_colour       *palette;
  1784.  
  1785.   assert(p);
  1786.   assert(p->in_palette.colours);
  1787.   assert(p->buffer);
  1788.  
  1789.   palette = p->in_palette.colours;
  1790.   source = p->buffer;
  1791.   line_length = p->line_length;
  1792.   width = p->pixel_width;
  1793.   for (y= p->pixel_height; (y > 0); y--) {
  1794.     dest = (bits *)source;
  1795.     /*
  1796.      * note HAVE to work from right to left because source == dest
  1797.      */
  1798.     for (x= width - 1; (x >= 0); x--) {
  1799.       dest[x] = LSR(palette[source[x]], 8);
  1800.     }
  1801.     source += line_length;
  1802.   }
  1803.   return FALSE;
  1804. }
  1805.  
  1806.  
  1807.  
  1808. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1809.  * output filename in a `nice' manner into a 15 character field
  1810.  */
  1811.  
  1812. static void log_filename(
  1813.                 const char      *name) {
  1814.   int   n;
  1815.  
  1816.   assert(name);
  1817.   n = strlen(name);
  1818.   if (n < 15) {
  1819.     fprintf(Glog, "%15s", name);
  1820.   } else {
  1821.     fprintf(Glog, "\x8c%s", name + n - 14);
  1822.   }
  1823. }
  1824.  
  1825.  
  1826.  
  1827. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1828.  */
  1829.  
  1830. static int stricmp(
  1831.                 const char      *sleft,
  1832.                 const char      *sright) {
  1833.   const char    *left, *right;
  1834.   int           i;
  1835.  
  1836.   assert(sleft);
  1837.   assert(sright);
  1838.   left = sleft;
  1839.   right = sright;
  1840.   if (left == right) {
  1841.     return 0;                                           /* need this check for assert code */
  1842.   }
  1843.   while (*left) {
  1844.     if (*right == '\0') {
  1845.       return *left;                                     /* left > right */
  1846.     }
  1847.     assert(left != sright);                             /* string overlap! */
  1848.     assert(right != sleft);                             /* string overlap! */
  1849.     i = tolower(*left) - tolower(*right);
  1850.     if (i != 0) {
  1851.       return i;                                         /* left != right */
  1852.     }
  1853.     right++;
  1854.     left++;
  1855.   }
  1856.   return -*right;                                       /* left <= right */
  1857. }
  1858.  
  1859.  
  1860.  
  1861.  
  1862. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1863.  * fills out a palette with entries for the default palette that the wimp assumes for the given
  1864.  * log2 bits per pixel specified
  1865.  */
  1866.  
  1867. static void default_wimppalette(
  1868.                 os_colour       *palette,
  1869.                 int             lb_bpp) {
  1870.   static const os_colour  default_wimp_palette_4bpp[16] = {
  1871.                 os_COLOUR_WHITE,                        /* 0 */
  1872.                 os_COLOUR_VERY_LIGHT_GREY,              /* 1 */
  1873.                 os_COLOUR_LIGHT_GREY,                   /* 2 */
  1874.                 os_COLOUR_MID_LIGHT_GREY,               /* 3 */
  1875.                 os_COLOUR_MID_DARK_GREY,                /* 4 */
  1876.                 os_COLOUR_DARK_GREY,                    /* 5 */
  1877.                 os_COLOUR_VERY_DARK_GREY,               /* 6 */
  1878.                 os_COLOUR_BLACK,                        /* 7 */
  1879.                 os_COLOUR_DARK_BLUE,                    /* 8 */
  1880.                 os_COLOUR_LIGHT_YELLOW,                 /* 9 */
  1881.                 os_COLOUR_LIGHT_GREEN,                  /* a */
  1882.                 os_COLOUR_LIGHT_RED,                    /* b */
  1883.                 os_COLOUR_CREAM,                        /* c */
  1884.                 os_COLOUR_DARK_GREEN,                   /* d */
  1885.                 os_COLOUR_ORANGE,                       /* e */
  1886.                 os_COLOUR_LIGHT_BLUE};                  /* f */
  1887.   static const os_colour  default_wimp_palette_2bpp[4] = {
  1888.                 os_COLOUR_WHITE,                        /* 0 */
  1889.                 os_COLOUR_LIGHT_GREY,                   /* 1 */
  1890.                 os_COLOUR_MID_DARK_GREY,                /* 2 */
  1891.                 os_COLOUR_BLACK};                       /* 3 */
  1892.   static const os_colour  default_wimp_palette_1bpp[2] = {
  1893.                 os_COLOUR_WHITE,                        /* 0 */
  1894.                 os_COLOUR_BLACK};                       /* 1 */
  1895.   static const os_colour        *defaults[3] = {
  1896.                 default_wimp_palette_1bpp,
  1897.                 default_wimp_palette_2bpp,
  1898.                 default_wimp_palette_4bpp};
  1899.  
  1900.   assert(palette);
  1901.  
  1902.   switch (lb_bpp) {
  1903.   case 0:
  1904.   case 1:
  1905.   case 2:
  1906.     memcpy(palette, defaults[lb_bpp], sizeof(os_colour) << (1 << lb_bpp));
  1907.     break;
  1908.   case 3:
  1909.     {
  1910.       int       red, grn, blu, tnt, idx;
  1911.  
  1912.       for (idx= 0; (idx < 256); idx++) {
  1913.         /* idx == %BGgRbrTt */
  1914.         red = ((idx & 0x10) >> 1) | ((idx & 0x04) >> 0);
  1915.         grn = ((idx & 0x40) >> 3) | ((idx & 0x20) >> 3);
  1916.         blu = ((idx & 0x80) >> 4) | ((idx & 0x08) >> 1);
  1917.         tnt = idx & 0x03;
  1918.         red |= tnt; red |= red << 4;                    /* expand 4 bit -> 8 bit */
  1919.         grn |= tnt; grn |= grn << 4;
  1920.         blu |= tnt; blu |= blu << 4;
  1921.         palette[idx] = (red << os_RSHIFT) | (grn << os_GSHIFT) | (blu << os_BSHIFT);
  1922.       }
  1923.     }
  1924.     break;
  1925.   default:
  1926.     assert(!"internal error");
  1927.   }
  1928. }
  1929.  
  1930.  
  1931.  
  1932. /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1933.  */
  1934.  
  1935.  
  1936. static void process_cli(
  1937.                 char            **argv) {
  1938.  
  1939.   char          *cli = NULL;
  1940.   char          *default_params;
  1941.   char          *arg;
  1942.   int           i;
  1943.  
  1944.   assert(argc >= 0);
  1945.   assert(argv);
  1946.  
  1947.   /*
  1948.    * note we NEED to slurp in C-parsed arguments because
  1949.    * a) os_get_env() will include any '>' redirection bits
  1950.    * b) os_get_env() will not handle long command lines
  1951.    *
  1952.    * put in environment variable args first so cli can overide 'em
  1953.    *
  1954.    * use our own command line parsing because os_read_args() does not like
  1955.    * duplicate options
  1956.    */
  1957.  
  1958.   default_params = getenv(GIF2RPC_PARAMS);
  1959.   if (default_params) {
  1960.     add_arg(&cli, default_params);
  1961.   }
  1962.   for (i= 1; (argv[i]); i++) {
  1963.     add_arg(&cli, argv[i]);
  1964.   }
  1965.  
  1966.   #define NEXTARG strtok(NULL, " \t")
  1967.   #define BOOLEANARG(ARG) } else if (stricmp(arg, "-" # ARG) == 0) { Gargs . ARG ## _p = !Gargs . ARG ## _p;
  1968.   #define STRINGARG(ARG) } else if (stricmp(arg, "-" # ARG) == 0) { arg = NEXTARG; if (arg) Gargs . ARG = arg; else { Gargs.help_p = TRUE; break; }
  1969.   #define INTEGERARG(ARG) } else if (stricmp(arg, "-" # ARG) == 0) { arg = NEXTARG; if (arg) Gargs . ARG = (int)strtol(arg, NULL, 10); else { Gargs.help_p = TRUE; break; }
  1970.   #define DEFAULTARG(ARG) } else if (!Gargs . ARG) { Gargs . ARG = arg;
  1971.  
  1972.   for (arg= strtok(cli, " \t"); (arg); arg= NEXTARG) {
  1973.     if (FALSE) {
  1974.     STRINGARG(gif)
  1975.     STRINGARG(sprite)
  1976.     STRINGARG(filter)
  1977.     STRINGARG(name)
  1978.     STRINGARG(log)
  1979.     BOOLEANARG(help)
  1980.     BOOLEANARG(square)
  1981.     BOOLEANARG(stats)
  1982.     BOOLEANARG(fussy)
  1983.     BOOLEANARG(accurate)
  1984.     BOOLEANARG(force_66)
  1985.     BOOLEANARG(quiet)
  1986.     BOOLEANARG(hourglass)
  1987.     BOOLEANARG(square)
  1988.     BOOLEANARG(autoname)
  1989.     INTEGERARG(bpp)
  1990.     INTEGERARG(verbose)
  1991.     } else {
  1992.       if (arg[0] == '-') {
  1993.         /*
  1994.          * unrecognised option
  1995.          */
  1996.         Gargs.help_p = TRUE;
  1997.         break;
  1998.       }
  1999.       if (FALSE) {
  2000.       DEFAULTARG(gif)
  2001.       DEFAULTARG(sprite)
  2002.       DEFAULTARG(filter)
  2003.       DEFAULTARG(log)
  2004.       DEFAULTARG(name)
  2005.       } else {
  2006.         /*
  2007.          * not an implicit argument
  2008.          */
  2009.         Gargs.help_p = TRUE;
  2010.         break;
  2011.       }
  2012.     }
  2013.   }
  2014.  
  2015.   if ((Gargs.help_p)
  2016.       || (Gargs.gif == NULL)
  2017.       || (Gargs.sprite == NULL)) {
  2018.     fprintf(stderr,
  2019.              "gif2rpc: invalid command line `%s'\n"
  2020.              "gif2rpc [-gif] <file> [-sprite] <file> [-filter] <name> [-log] <file> "
  2021.              "[-bpp <n>] [-verbose <n>] "
  2022.              "[-quiet] [-hourglass] [-accurate] [-force_66] "
  2023.              "[-name <sprite>]\n"
  2024.              "gif          gif file to convert\n"
  2025.              "sprite       name for output sprite file\n"
  2026.              "bpp          bits-per-pixel for output sprite [-1/0/1/2/4/8/16/32]\n"
  2027.              "              0 => output with gif's palette, as 1/2/4/8 bpp sprite\n"
  2028.              "              -1 => output as current mode with current palette\n"
  2029.              "              >0 => output with default (no) palette in specified bpp\n"
  2030.              "quiet        do not print anything if an error occurs --- see log, verbose\n"
  2031.              "verbose      how much detail do you want 0/1/2? --- see log, quiet\n"
  2032.              "log          file to which error and status information is sent --- see quiet, verbose\n"
  2033.              "hourglass    enable hourglass\n"
  2034.              "name         name of sprite in file <forced to lower case>\n"
  2035.              "autoname     name of sprite in file derives from the file name\n"
  2036.              "square       ignore aspect ratio (if output bpp >= 16)\n"
  2037.              "accurate     use slower code to output 32K/<= 256 colour images\n"
  2038.              "force_66     force use of 66-bit accuracy when outputing 32K images\n"
  2039.              "filter       one of: ",
  2040.              cli);
  2041.     for (i= 0; (i < COUNT(Gfilters)); i++) {
  2042.       fprintf(stderr, "%s%s", Gfilters[i].name, (i == (COUNT(Gfilters)-1)) ? "\n": ", ");
  2043.     }
  2044.     exit(EXIT_SUCCESS);
  2045.   }
  2046.   /*
  2047.    * note can't free(cli) here because Gargs contains pointers into it for
  2048.    * its' string arguments
  2049.    */
  2050. }
  2051.